package com.cloudspace.yunloan.player;

import android.content.Context;
import android.media.AudioManager;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;

import com.cloudspace.yunloan.R;
import com.cloudspace.yunloan.YunloanApp;
import com.cloudspace.yunloan.player.base.AbsMediaPlayer;
import com.cloudspace.yunloan.utils.AppPreferences;
import com.cloudspace.yunloan.utils.ObjUtil;

import java.lang.ref.WeakReference;

public class DefaultVideoPlayerSurfaceView extends SurfaceView {
    private static final String SUB_TAG = "DefaultVideoPlayerSurfaceView: ";
    public static final int MSG_NEXT_URL = 0x10001;
    public static final String AVALIABLE_URL_KEY = "avaliable_url_key";
    private static DefaultVideoPlayerSurfaceView instance;
    private static int mType = AbsMediaPlayer.TYPE_EXO;

    private boolean mIsPreLoadVideo;
    private boolean mIsPrepared;
    private boolean mIsSurfaceCreated;
    private boolean mIsPausing;

    private int mContainerWidth = -1;
    private int mContainerHeight = -1;
    private int mVideoWidth = -1;
    private int mVideoHeight = -1;
    private int mListenerHashCode = -1;
    private String mVideoUrl = "";
    private String mPreLoadVideoUrl = "";
    private String mCurrentUrl = "";

    private AbsMediaPlayer mMediaPlayer;
    private AudioManager mAudioMgr;
    private AncillaryOperationHandler mUiHandler;


    public static DefaultVideoPlayerSurfaceView getInstance(Context context, int type) {
        if (instance == null) {
            instance = new DefaultVideoPlayerSurfaceView(context.getApplicationContext());
            instance.initView(type);
        }
        return instance;
    }

    public static DefaultVideoPlayerSurfaceView getInstance(Context context) {
        if (instance == null) {
            instance = new DefaultVideoPlayerSurfaceView(context.getApplicationContext());
            instance.initView(mType);
        }
        return instance;
    }

    private DefaultVideoPlayerSurfaceView(Context paramContext) {
        super(paramContext);
    }

    private void initView(int type) {
        mType = type;
        getHolder().addCallback(mSHCallback);
        getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    private SurfaceHolder mSurfaceHolder;
    private SurfaceHolder.Callback mSHCallback = new SurfaceHolder.Callback() {
        @Override
        public void surfaceChanged(SurfaceHolder paramSurfaceHolder,
                                   int paramInt1, int width, int height) {
            Log.v(Constants.MEDIAPLAYER_LOG_TAG,
                    "Surface changed, width=" + width + ", height=" + height);
            mSurfaceHolder = paramSurfaceHolder;
            if (mMediaPlayer != null) {
                mMediaPlayer.setDisplay(paramSurfaceHolder);
            }
        }

        @Override
        public void surfaceCreated(SurfaceHolder paramSurfaceHolder) {
            Log.v(Constants.MEDIAPLAYER_LOG_TAG, "Surface created.");
            mSurfaceHolder = paramSurfaceHolder;
            mIsSurfaceCreated = true;
            init();
            if (mMediaPlayer != null) {
                mMediaPlayer.setDisplay(paramSurfaceHolder);
            }
        }

        @Override
        public void surfaceDestroyed(SurfaceHolder paramSurfaceHolder) {
            Log.v(Constants.MEDIAPLAYER_LOG_TAG, "Surface destoryed.");
            mIsSurfaceCreated = false;
            if (mMediaPlayer != null) {
                mMediaPlayer.setDisplay(null);
            }
        }
    };

    public void clearDisplay() {
        if (mMediaPlayer != null) {
            mMediaPlayer.setDisplay(null);
        }
    }

    public void setDisplay() {
        if (mMediaPlayer != null) {
            mMediaPlayer.setDisplay(mSurfaceHolder);
        }
    }

    private void init() {
        Log.v(Constants.MEDIAPLAYER_LOG_TAG, SUB_TAG + "Media Player init.");
        setVisibility(View.VISIBLE);
        if (null == mMediaPlayer) {
            mMediaPlayer = AbsMediaPlayer.getMediaPlayer(YunloanApp.getApp().getApplicationContext(), mType);
        }
        mAudioMgr = (AudioManager) YunloanApp.getApp().getSystemService(Context.AUDIO_SERVICE);
        mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener);
        mMediaPlayer.setOnPreparedListener(onPreparedListener);
        mMediaPlayer.setOnCompletionListener(onCompletionListener);
        mMediaPlayer.setOnBufferingUpdateListener(onBufferingUpdateListener);
        mMediaPlayer.setOnErrorListener(onErrorListener);
        mMediaPlayer.setOnPlayStateChangeListener(onPlayStateChangeListener);
        mMediaPlayer.setOnPlaySwitchListener(onPlaySwitchListener);
        if (mUiHandler == null) {
            mUiHandler = new AncillaryOperationHandler(this);
        }
    }

    int aspectMode = 0; // 0: fill 保持比例，显示全屏，截取宽高 1: fit 保持比例，显示完整；
    public void setAspectMode(int aspectMode) {
        this.aspectMode = aspectMode;
        requestLayout();
    }

    private void setVideoSize(int width, int height) {
        if (!mIsSurfaceCreated
                || width == 0
                || height == 0) {
        } else {
            mContainerWidth = ((ViewGroup) getParent()).getWidth();
            mContainerHeight = ((ViewGroup) getParent()).getHeight();

            ViewGroup.LayoutParams params = getLayoutParams();

            if (mContainerHeight < height || mContainerWidth < width) {
                params.height = mContainerHeight;
                params.width = mContainerWidth;

            } else if (width > 0 && height > 0) {
                params.height = height;
                params.width = width;
            }

            setLayoutParams(params);

        }
        if (getParent() != null) {
            ((ViewGroup) getParent()).requestLayout();
        }
        requestLayout();
    }

    public void setVideoURI(String path) {
        Log.v(Constants.MEDIAPLAYER_LOG_TAG, SUB_TAG + "set play uri, uri = " + path);
        if (TextUtils.isEmpty(path)) {
            Log.d(Constants.MEDIAPLAYER_LOG_TAG, SUB_TAG + "Path is empty.");
            return;
        }

        stop();
        init();
        if (!TextUtils.isEmpty(path) && path.equals(mCurrentUrl) && mIsPrepared == false) {
            Log.v(Constants.MEDIAPLAYER_LOG_TAG,
                    SUB_TAG + "Play address error or not prepared.");
            return;
        }
        if (mIsPreLoadVideo && mPreLoadVideoUrl.equals(path)) {
            Log.v(Constants.MEDIAPLAYER_LOG_TAG, SUB_TAG + "Preload video.");
            if (mIsPrepared) {
                setPreLoadVideo(false);
                mMediaPlayer.start();
            }
            return;
        }
        mCurrentUrl = path;
        _prepare(path);
    }

    public void setPreLoadVideo(boolean isPreLoadVideo) {
        this.mIsPreLoadVideo = isPreLoadVideo;
    }

    public void resume() {
        if ((mMediaPlayer != null) && (!mMediaPlayer.isPlaying())) {
            mIsPausing = false;
            mMediaPlayer.start();
        }
    }

    public void start() {
        Log.v(Constants.MEDIAPLAYER_LOG_TAG, SUB_TAG + "Start play operation.");
        if (mMediaPlayer != null && mIsPrepared) {
            requestAudioFocus();
            mMediaPlayer.start();
            setPreLoadVideo(false);
        }
    }

    public void pause() {
        Log.v(Constants.MEDIAPLAYER_LOG_TAG, SUB_TAG + "Pause play operation.");
        if ((mMediaPlayer != null) && (mMediaPlayer.isPlaying())) {
            mIsPausing = true;
            mMediaPlayer.pause();
        }
    }

    public void stop() {
        Log.v(Constants.MEDIAPLAYER_LOG_TAG, SUB_TAG + "Stop play operation.");
        if (TextUtils.isEmpty(mCurrentUrl)) {
            return;
        }
        mCurrentUrl = "";
//        setVisibility(View.GONE);
        if (mMediaPlayer != null && mIsPrepared) {
            mIsPausing = false;
            mMediaPlayer.stop();
        }
        if (mMediaPlayer != null) {
            mMediaPlayer.reset();
        }
        resetStatus();
        releaseAudioFocus();
        if (mUiHandler != null) {
            mUiHandler.removeCallbacksAndMessages(null);
            mUiHandler = null;
        }
    }

    public void reset() {
        if (mMediaPlayer != null) {
            mMediaPlayer.reset();
        }
    }

    public void release() {
        if (mMediaPlayer != null) {
            mMediaPlayer.release();
        }
        resetVideoView();
    }


    public void seekTo(int msec) {
        Log.v(Constants.MEDIAPLAYER_LOG_TAG, SUB_TAG
                + "Seek to play: milliseconds=" + msec);
        if (mMediaPlayer != null
                && mIsPrepared
                && (mMediaPlayer.getPlayState() == AbsMediaPlayer.STATE_PLAYING
                || mMediaPlayer.getPlayState() == AbsMediaPlayer.STATE_SEEKING
                || mMediaPlayer.getPlayState() == AbsMediaPlayer.STATE_PAUSED)) {
//            int bufferSize = getBufferedProgress() * getDuration() / 100;
//            Log.debugLog(Constants.MEDIAPLAYER_LOG_TAG, SUB_TAG
//                    + "Buffer milliseconds=" + bufferSize);
//            if (msec > bufferSize) {
//                msec = bufferSize;
//            }
            Log.d(Constants.MEDIAPLAYER_LOG_TAG, SUB_TAG
                    + "Real seek: milliseconds=" + msec);
            mMediaPlayer.seekTo(msec);
        }
    }

    public void reset(int hashcode) {
        Log.v(Constants.MEDIAPLAYER_LOG_TAG, SUB_TAG + "Play reseted operation");
        if (hashcode == mListenerHashCode) {
            setOnPreparedListener(null);
            setOnErrorListener(null);
            setOnCompletionListener(null);
            setOnBufferingUpdateListener(null);
            setOnPlayStateChangeListener(null);
        }
    }

    public void unregisterListeners() {
        Log.v(Constants.MEDIAPLAYER_LOG_TAG, "clear all listeners");
        mMediaPlayer.setOnVideoSizeChangedListener(null);
        mMediaPlayer.setOnPreparedListener(null);
        mMediaPlayer.setOnCompletionListener(null);
        mMediaPlayer.setOnBufferingUpdateListener(null);
        mMediaPlayer.setOnErrorListener(null);
        mMediaPlayer.setOnPlayStateChangeListener(null);
        mMediaPlayer.setOnPlaySwitchListener(null);

    }

    public void resetStatus() {
        Log.v(Constants.MEDIAPLAYER_LOG_TAG, SUB_TAG + "Reset play status operation");
        mIsPrepared = false;
        mIsPreLoadVideo = false;
    }

    public void resetVideoView() {
        Log.v(Constants.MEDIAPLAYER_LOG_TAG, SUB_TAG + "Reset video view operation");
        if (instance != null) {
            instance = null;
        }
    }

    public boolean isPlaying() {
        return mMediaPlayer != null && mMediaPlayer.isPlaying();
    }

    public boolean isPausing() {
        return mIsPausing;
    }

    public int getType() {
        return mType;
    }

    public int getCurrentPosition() {
        if (mMediaPlayer != null && mIsPrepared) {
            int pos = mMediaPlayer.getCurrentPosition();
            Log.d(Constants.MEDIAPLAYER_LOG_TAG, SUB_TAG + "Get current play Position form native=" + pos);
            return pos;
        }
        return 0;
    }

    public int getDuration() {
        Log.d(Constants.MEDIAPLAYER_LOG_TAG, SUB_TAG + "Get duration.");
        if (mMediaPlayer != null && mIsPrepared) {
            int dur = mMediaPlayer.getDuration();
            Log.d(Constants.MEDIAPLAYER_LOG_TAG, SUB_TAG + "dur=" + dur);
            return dur;
        }
        return 0;
    }

    public int getBufferedProgress() {
        Log.d(Constants.MEDIAPLAYER_LOG_TAG,
                SUB_TAG + "Get Buffer progress.");
        int progress = 0;
        if (mMediaPlayer != null && mIsPrepared) {
            progress = mMediaPlayer.getPlayerBufferedProgress();
        }
        Log.d(Constants.MEDIAPLAYER_LOG_TAG,
                SUB_TAG + "progress=" + progress);
        return progress;
    }


    public AbsMediaPlayer getMediaPlayer() {
        return mMediaPlayer;
    }


    private void _prepare(final String path) {
        Log.v(Constants.MEDIAPLAYER_LOG_TAG,
                SUB_TAG + "Pre execute before playing.");
        if ((!mIsSurfaceCreated
                || mMediaPlayer.getPlayState() == AbsMediaPlayer.STATE_PREPARING)
                && mUiHandler != null) {
            Log.d(Constants.MEDIAPLAYER_LOG_TAG, SUB_TAG  + ", is surface created:"
                    + new Boolean(mIsSurfaceCreated).toString() + ", play state = "
                    + mMediaPlayer.getPlayState() + ", handler = " + mUiHandler);
            mUiHandler.postDelayed(new Runnable() {

                @Override
                public void run() {
                    Log.d(Constants.MEDIAPLAYER_LOG_TAG,
                            SUB_TAG + "Not ready, wait 200 miliseconds and try again.");
                    _prepare(path);
                }
            }, 200);
            return;
        }
        try {
            mMediaPlayer.setDataSource(path);
            try {
                mMediaPlayer.prepareAsync();
            } catch (IllegalStateException e) {
                e.printStackTrace();
                Log.e(Constants.MEDIAPLAYER_LOG_TAG,
                        SUB_TAG + "Illeagal state exceptiom when prepareAsync.");
                mMediaPlayer.setPlayState(AbsMediaPlayer.STATE_ERROR);
            } catch (Exception e) {
                Log.e(Constants.MEDIAPLAYER_LOG_TAG,
                        SUB_TAG + "Exception before playing.");
                e.printStackTrace();
                mMediaPlayer.setPlayState(AbsMediaPlayer.STATE_ERROR);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
            Log.e(Constants.MEDIAPLAYER_LOG_TAG,
                    SUB_TAG + "Exception when preprocessing." + ex.getMessage());
            mMediaPlayer.setPlayState(AbsMediaPlayer.STATE_ERROR);
        }
    }

    // setVideoURI need UI operation, add one handler, remove it if more smart idea existed.
    private static class AncillaryOperationHandler extends Handler {
        WeakReference<DefaultVideoPlayerSurfaceView> changbaPlayerWeakReference;

        public AncillaryOperationHandler(DefaultVideoPlayerSurfaceView player) {
            changbaPlayerWeakReference = new WeakReference<DefaultVideoPlayerSurfaceView>(player);
        }

        @Override
        public void handleMessage(Message msg) {
            final DefaultVideoPlayerSurfaceView changbaPlayer = changbaPlayerWeakReference.get();
            if (changbaPlayer != null) {
                switch (msg.what) {
                    case MSG_NEXT_URL:
                        changbaPlayer.setVideoURI(msg.getData().getString(AVALIABLE_URL_KEY));
                        break;
                }
            } else {
                Log.e(Constants.MEDIAPLAYER_LOG_TAG, SUB_TAG
                        + "ChangbaPlayer WeakReference is null.");
            }
        }
    }

    // For outter listener
    AbsMediaPlayer.OnPreparedListener mOnPreparedListener;
    AbsMediaPlayer.OnCompletionListener mOnCompletionListener;
    AbsMediaPlayer.OnErrorListener mOnErrorListener;
    AbsMediaPlayer.OnBufferingUpdateListener mOnBufferingUpdateListener;
    AbsMediaPlayer.OnPlayStateChangeListener mPlayStateChangeListener;
    AbsMediaPlayer.OnPlaySwitchListener mPlaySwitchListener;

    public void setOnPreparedListener(AbsMediaPlayer.OnPreparedListener mOnPreparedListener) {
        this.mOnPreparedListener = mOnPreparedListener;
        if (mOnPreparedListener != null) {
            mListenerHashCode = mOnPreparedListener.hashCode();
        } else {
            mListenerHashCode = -1;
        }
    }

    public void setOnCompletionListener(AbsMediaPlayer.OnCompletionListener mOnCompletionListener) {
        this.mOnCompletionListener = mOnCompletionListener;
    }

    public void setOnErrorListener(AbsMediaPlayer.OnErrorListener mOnErrorListener) {
        this.mOnErrorListener = mOnErrorListener;
    }

    public void setOnBufferingUpdateListener(AbsMediaPlayer.OnBufferingUpdateListener listener) {
        mOnBufferingUpdateListener = listener;
    }

    public void setOnPlayStateChangeListener(AbsMediaPlayer.OnPlayStateChangeListener listener) {
        mPlayStateChangeListener = listener;
    }

    public void setOnPlaySwitchListener(AbsMediaPlayer.OnPlaySwitchListener listener) {
        mPlaySwitchListener = listener;
    }

    // For media player listener
    AbsMediaPlayer.OnPreparedListener onPreparedListener = new AbsMediaPlayer.OnPreparedListener() {
        @Override
        public void onPrepared(Object mp) {
            Log.d(Constants.MEDIAPLAYER_LOG_TAG, SUB_TAG + "Media Player onPrepared.");
            if (mp instanceof android.media.MediaPlayer) {
                mVideoWidth = ((android.media.MediaPlayer) mp).getVideoWidth();
                mVideoHeight = ((android.media.MediaPlayer) mp)
                        .getVideoHeight();
            }
            setVideoSize(mVideoWidth, mVideoHeight);
            mIsPrepared = true;

            if (null != mOnPreparedListener) {
                mOnPreparedListener.onPrepared(mp);
            }

            start();
        }
    };
    AbsMediaPlayer.OnCompletionListener onCompletionListener = new AbsMediaPlayer.OnCompletionListener() {
        @Override
        public void onCompletion(Object mp) {
            Log.v(Constants.MEDIAPLAYER_LOG_TAG, SUB_TAG + "Media Player onCompletion.");
            if (null != mOnCompletionListener) {
                mOnCompletionListener.onCompletion(mp);
            }
        }
    };

    AbsMediaPlayer.OnErrorListener onErrorListener = new AbsMediaPlayer.OnErrorListener() {
        @Override
        public boolean onError(Object mp, int what, int extra) {
            Log.e(Constants.MEDIAPLAYER_LOG_TAG,
                    SUB_TAG + "Media Player onError, what=" + what + ", extra=" + extra);
//            ToastMaker.showToastShort(getResources().getString(R.string.player_error_system_player_tip)
//                    + "(" + what + ", " + extra + ")");  这种错误码的提示不该让用户看到
            if (null != mOnErrorListener) {
                return mOnErrorListener.onError(mp, what, extra);
            }
            return false;
        }
    };

    AbsMediaPlayer.OnBufferingUpdateListener onBufferingUpdateListener = new AbsMediaPlayer.OnBufferingUpdateListener() {
        @Override
        public void onBufferingUpdate(Object mp, int percent) {
            Log.d(Constants.MEDIAPLAYER_LOG_TAG,
                    SUB_TAG + "Media Player buffer updated, percent=" + percent);
            if (null != mOnBufferingUpdateListener) {
                mOnBufferingUpdateListener.onBufferingUpdate(mp, percent);
            }
        }
    };

    AbsMediaPlayer.OnPlayStateChangeListener onPlayStateChangeListener = new AbsMediaPlayer.OnPlayStateChangeListener() {
        @Override
        public void onPlayStateChange(int playState) {
            Log.d(Constants.MEDIAPLAYER_LOG_TAG,
                    SUB_TAG + "Media Player state changed, playState=" + playState);
            if (playState == AbsMediaPlayer.STATE_PREPARED) {
                mIsPrepared = true;
            }
            if (null != mPlayStateChangeListener) {
                mPlayStateChangeListener.onPlayStateChange(playState);
            }
        }
    };

    AbsMediaPlayer.OnPlaySwitchListener onPlaySwitchListener = new AbsMediaPlayer.OnPlaySwitchListener() {
        @Override
        public void onPlaySwitch(int direction, boolean isForcedMusic) {
            Log.d(Constants.MEDIAPLAYER_LOG_TAG,
                    SUB_TAG + "Media Player onSwitched, direction=" + direction);
            if (null != mPlaySwitchListener) {
                mPlaySwitchListener.onPlaySwitch(direction, isForcedMusic);
            }
        }
    };

    AbsMediaPlayer.OnVideoSizeChangedListener mSizeChangedListener = new AbsMediaPlayer.OnVideoSizeChangedListener() {
        @Override
        public void onVideoSizeChanged(Object mp, int arg1, int arg2) {
            Log.d(Constants.MEDIAPLAYER_LOG_TAG,
                    SUB_TAG + "Media Player onSizeChanged, arg1=" + arg1 + ", arg2=" + arg2);
            if (mp instanceof android.media.MediaPlayer) {
                mVideoWidth = ((android.media.MediaPlayer) mp).getVideoWidth();
                mVideoHeight = ((android.media.MediaPlayer) mp)
                        .getVideoHeight();
            }

            setBackgroundColor(getResources().getColor(R.color.transparent));
            if (mVideoWidth != 0 && mVideoHeight != 0) {
//                _fixedSize(mVideoWidth, mVideoHeight);
                requestLayout();
            }
        }
    };

    private void requestAudioFocus() {
        Log.d(Constants.MEDIAPLAYER_LOG_TAG, SUB_TAG + "Request audio focus.");
        if (mAudioMgr != null) {
            mAudioMgr.requestAudioFocus(mAudioFocusListener,
                    AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
        }
    }

    private void releaseAudioFocus() {
        Log.d(Constants.MEDIAPLAYER_LOG_TAG, SUB_TAG + "Release audio focus.");
        if (mAudioMgr != null) {
            mAudioMgr.abandonAudioFocus(mAudioFocusListener);
        }
    }

    private boolean mPausedByTransientLossOfFocus = false;
    private AudioManager.OnAudioFocusChangeListener mAudioFocusListener = new AudioManager.OnAudioFocusChangeListener() {
        public void onAudioFocusChange(int focusChange) {
            switch (focusChange) {
                case AudioManager.AUDIOFOCUS_GAIN:
                    Log.d(Constants.MEDIAPLAYER_LOG_TAG, "Audio focus gain.");
                    if (!isPlaying() && mPausedByTransientLossOfFocus) {
                        mPausedByTransientLossOfFocus = false;
                        start();
                    }
                    break;
                case AudioManager.AUDIOFOCUS_LOSS:
                    Log.d(Constants.MEDIAPLAYER_LOG_TAG, "Audio focus loss.");
                    if (isPlaying()) {
                        mPausedByTransientLossOfFocus = false;
                        pause();
                    }
                    break;
                case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
                    Log.d(Constants.MEDIAPLAYER_LOG_TAG, "Audio focus loss transient.");
                    if (isPlaying()) {
                        mPausedByTransientLossOfFocus = true;
                        pause();
                    }
                    break;
                case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
                    Log.d(Constants.MEDIAPLAYER_LOG_TAG, "Audio focus can duck.");
                    if (isPlaying()) {
                        mPausedByTransientLossOfFocus = true;
                        pause();
                    }
                    break;
            }
        }
    };

}
